package com.hexu.sharedb.config;


import cn.hutool.core.io.resource.ClassPathResource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
import org.apache.shardingsphere.spring.boot.ShardingSphereAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.AbstractApplicationContext;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.InputStream;
import java.util.Map;

/**
 * <p> sharding-jdbc集成动态数据源配置 </p>
 *
 * @author zhengqingya
 * @description 使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解，切换数据源
 * ex: 切换为sharding-jdbc数据源 => @DS(DataSourceConfig.SHARDING_DATA_SOURCE_NAME)
 * @date 2022/11/2 10:13
 */
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, ShardingSphereAutoConfiguration.class})
@Slf4j
public class DataSourceConfig {
	/**
	 * 分表数据源名称
	 */
	public static final String SHARDING_DATA_SOURCE_NAME = "sharding-data-source";
	@Autowired
	AbstractApplicationContext context;
	/**
	 * 动态数据源配置项
	 */
	@Autowired
	private DynamicDataSourceProperties properties;

	/**
	 * sharding-jdbc有四种数据源，需要根据业务注入不同的数据源
	 * <p>
	 * 1.未使用分片, 脱敏的名称(默认): shardingDataSource;
	 * 2.主从数据源: masterSlaveDataSource;
	 * 3.脱敏数据源：encryptDataSource;
	 * 4.影子数据源：shadowDataSource
	 */
	@Lazy
	@Resource(description = "shardingDataSource")
	private DataSource shardingDataSource;

	@SneakyThrows
	@Bean
	public DynamicDataSourceProvider dynamicDataSourceProvider() {
		ClassPathResource classPathResource = new ClassPathResource("application-sharding-tables.yml");
		InputStream stream = classPathResource.getStream();
		shardingDataSource =  YamlShardingSphereDataSourceFactory.createDataSource(IOUtils.toByteArray(stream));

		Map<String, DataSourceProperty> datasourceMap = properties.getDatasource();
		return new AbstractDataSourceProvider() {
			@Override
			public Map<String, DataSource> loadDataSources() {
				Map<String, DataSource> dataSourceMap = createDataSourceMap(datasourceMap);
				// 将 shardingjdbc 管理的数据源也交给动态数据源管理
				dataSourceMap.put(SHARDING_DATA_SOURCE_NAME, shardingDataSource);
				return dataSourceMap;
			}
		};
	}

	/**
	 * 将动态数据源设置为首选的
	 * 当spring存在多个数据源时, 自动注入的是首选的对象
	 * 设置为主要的数据源之后，就可以支持sharding-jdbc原生的配置方式了
	 */
	@SneakyThrows
	@Primary
	@Bean
	public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {

		DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
		dataSource.setPrimary(properties.getPrimary());
		dataSource.setStrict(properties.getStrict());
		dataSource.setStrategy(properties.getStrategy());
		dataSource.setProvider(dynamicDataSourceProvider);
		dataSource.setP6spy(properties.getP6spy());
		dataSource.setSeata(properties.getSeata());
		return dataSource;
	}
}
